import * as Cesium from 'cesium';
//@ts-ignore
const { PrimitiveRenderResources, ModelUtility, Cartesian3, Matrix4, ModelRenderResources, defined,
  //@ts-ignore
  NodeRenderResources, BoundingSphere, ModelDrawCommands
} = Cesium;
const scratchModelPositionMin = new Cartesian3();
const scratchModelPositionMax = new Cartesian3();
const scratchPrimitivePositionMin = new Cartesian3();
const scratchPrimitivePositionMax = new Cartesian3();
// 实例化的模型，如果原始数据有缩放，会导致包围盒计算出现问题
// 正常应该先缩放后在增加实例化包围盒大小
// 但是Cesium的代码是先增加实例化包围盒大小，再缩放，导致包围盒计算错误，这里修复一下
// 如果有实例化数据，先把实例化包围盒放大，再缩放
export class XBSJPrimitiveRenderResources extends PrimitiveRenderResources {
  //@ts-ignore
  constructor(nodeRenderResources: Cesium.NodeRenderResources, runtimePrimitive: Cesium.ModelRuntimePrimitive, nodeTransform: Matrix4) {
    super(nodeRenderResources, runtimePrimitive);
    if (!nodeRenderResources.runtimeNode.instancingTranslationMin || !nodeRenderResources.runtimeNode.instancingTranslationMax) return;
    const nodeTransformInverse = Matrix4.inverse(nodeTransform, new Matrix4())
    const tempPositionMinMax = ModelUtility.getPositionMinMax(
      runtimePrimitive.primitive,
      Matrix4.multiplyByPoint(nodeTransformInverse, nodeRenderResources.runtimeNode.instancingTranslationMin, scratchPrimitivePositionMin),
      Matrix4.multiplyByPoint(nodeTransformInverse, nodeRenderResources.runtimeNode.instancingTranslationMax, scratchPrimitivePositionMax),
    );
    //@ts-ignore
    this.positionMin = Cartesian3.clone(tempPositionMinMax.min, new Cartesian3());
    //@ts-ignore
    this.positionMax = Cartesian3.clone(tempPositionMinMax.max, new Cartesian3());
    //@ts-ignore
    this.boundingSphere = BoundingSphere.fromCornerPoints(this.positionMin, this.positionMax, new BoundingSphere());

  }
}
export function fixModelSceneGraph() {
  //@ts-ignore
  Cesium.ModelSceneGraph.prototype.buildDrawCommands = function (frameState) {
    const model = this._model;
    const modelRenderResources = new ModelRenderResources(model);

    // Reset the memory counts before running the pipeline
    model.statistics.clear();

    this.configurePipeline(frameState);
    const modelPipelineStages = this.modelPipelineStages;

    let i, j, k;
    for (i = 0; i < modelPipelineStages.length; i++) {
      const modelPipelineStage = modelPipelineStages[i];
      modelPipelineStage.process(modelRenderResources, model, frameState);
    }

    const modelPositionMin = Cartesian3.fromElements(
      Number.MAX_VALUE,
      Number.MAX_VALUE,
      Number.MAX_VALUE,
      scratchModelPositionMin,
    );
    const modelPositionMax = Cartesian3.fromElements(
      -Number.MAX_VALUE,
      -Number.MAX_VALUE,
      -Number.MAX_VALUE,
      scratchModelPositionMax,
    );

    for (i = 0; i < this._runtimeNodes.length; i++) {
      const runtimeNode = this._runtimeNodes[i];

      // If a node in the model was unreachable from the scene graph, there will
      // be no corresponding runtime node and therefore should be skipped.
      if (!defined(runtimeNode)) {
        continue;
      }

      runtimeNode.configurePipeline();
      const nodePipelineStages = runtimeNode.pipelineStages;

      const nodeRenderResources = new NodeRenderResources(
        modelRenderResources,
        runtimeNode,
      );

      for (j = 0; j < nodePipelineStages.length; j++) {
        const nodePipelineStage = nodePipelineStages[j];

        nodePipelineStage.process(
          nodeRenderResources,
          runtimeNode.node,
          frameState,
        );
      }

      const nodeTransform = runtimeNode.computedTransform;
      for (j = 0; j < runtimeNode.runtimePrimitives.length; j++) {
        const runtimePrimitive = runtimeNode.runtimePrimitives[j];

        runtimePrimitive.configurePipeline(frameState);
        const primitivePipelineStages = runtimePrimitive.pipelineStages;

        const primitiveRenderResources = new XBSJPrimitiveRenderResources(
          nodeRenderResources,
          runtimePrimitive,
          nodeTransform
        );

        for (k = 0; k < primitivePipelineStages.length; k++) {
          const primitivePipelineStage = primitivePipelineStages[k];

          primitivePipelineStage.process(
            primitiveRenderResources,
            runtimePrimitive.primitive,
            frameState,
          );
        }

        runtimePrimitive.boundingSphere = BoundingSphere.clone(
          //@ts-ignore
          primitiveRenderResources.boundingSphere,
          new BoundingSphere(),
        );

        const primitivePositionMin = Matrix4.multiplyByPoint(
          nodeTransform,
          //@ts-ignore
          primitiveRenderResources.positionMin,
          scratchPrimitivePositionMin,
        );
        const primitivePositionMax = Matrix4.multiplyByPoint(
          nodeTransform,
          //@ts-ignore
          primitiveRenderResources.positionMax,
          scratchPrimitivePositionMax,
        );

        Cartesian3.minimumByComponent(
          modelPositionMin,
          primitivePositionMin,
          modelPositionMin,
        );
        Cartesian3.maximumByComponent(
          modelPositionMax,
          primitivePositionMax,
          modelPositionMax,
        );

        const drawCommand = ModelDrawCommands.buildModelDrawCommand(
          primitiveRenderResources,
          frameState,
        );
        runtimePrimitive.drawCommand = drawCommand;
      }
    }

    this._boundingSphere = BoundingSphere.fromCornerPoints(
      modelPositionMin,
      modelPositionMax,
      new BoundingSphere(),
    );

    this._boundingSphere = BoundingSphere.transformWithoutScale(
      this._boundingSphere,
      this._axisCorrectionMatrix,
      this._boundingSphere,
    );

    this._boundingSphere = BoundingSphere.transform(
      this._boundingSphere,
      this._components.transform,
      this._boundingSphere,
    );

    model._boundingSphere = BoundingSphere.transform(
      this._boundingSphere,
      model.modelMatrix,
      model._boundingSphere,
    );

    model._initialRadius = model._boundingSphere.radius;
    model._boundingSphere.radius *= model._clampedScale;
  };
}